package com.springsource.a01beanfactory;

import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.annotation.AnnotationConfigUtils;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;



@Slf4j
public class TestBeanFactory {

    public static void main(String[] args) {
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
        // bean的定义 （class、scope、初始化方法、销毁方法）
        // 创建Config的BeanDefinition
        AbstractBeanDefinition singleton =
                BeanDefinitionBuilder.genericBeanDefinition(Config.class).setScope("singleton").getBeanDefinition();
        // BeanDefinition 注册到工厂中
        beanFactory.registerBeanDefinition("config", singleton);
        // 此时只有config，没有bean1、bean2
        for (String beanDefinitionName : beanFactory.getBeanDefinitionNames()) {
            log.warn("beanDefinitionName: {}", beanDefinitionName);
        }
        log.warn("===================================================================================================");
        // 给BeanFactory添加后置处理器，使bean工厂持有后置处理器
        AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);
        // 看一下加了哪些处理器
        for (String beanDefinitionName : beanFactory.getBeanDefinitionNames()) {
            log.warn("beanDefinitionName: {}", beanDefinitionName);
        }
        /**
         17:21:30.988 [main] DEBUG com.springsource.beanfactory.TestBeanFactory - beanDefinitionName: org.springframework.context.annotation.internalConfigurationAnnotationProcessor
         解析Configuration注解，实现@Bean调用
         17:21:30.988 [main] DEBUG com.springsource.beanfactory.TestBeanFactory - beanDefinitionName: org.springframework.context.annotation.internalAutowiredAnnotationProcessor
         17:21:30.988 [main] DEBUG com.springsource.beanfactory.TestBeanFactory - beanDefinitionName: org.springframework.context.annotation.internalCommonAnnotationProcessor
         17:21:30.988 [main] DEBUG com.springsource.beanfactory.TestBeanFactory - beanDefinitionName: org.springframework.context.event.internalEventListenerProcessor
         17:21:30.988 [main] DEBUG com.springsource.beanfactory.TestBeanFactory - beanDefinitionName: org.springframework.context.event.internalEventListenerFactory
         **/
        log.warn("调用BeanFactoryProcessor");
        // 调用处理器
        for (BeanFactoryPostProcessor value : beanFactory.getBeansOfType(BeanFactoryPostProcessor.class).values()) {
            value.postProcessBeanFactory(beanFactory);
        }
        // 再看一下注册的bean
        for (String beanDefinitionName : beanFactory.getBeanDefinitionNames()) {
            log.warn("beanDefinitionName: {}", beanDefinitionName);
        }
        log.warn("===================================================================================================");
        // 获取bean1
//        Bean1 bean1 = beanFactory.getBean(Bean1.class);
//        log.debug("bean1: {}", bean1);
//        log.debug("bean1.getBean2(): {}", bean1.getBean2());
        // 此时获取bean1没问题，但是bean1.getBean2()是null，说明依赖注入没有生效，需要调用bean后处理器
        // BeanPostProcessor针对bean的生命周期各个阶段提供扩展功能，例如@Autowired @Resource...

        // 使BeanFactory在实例化bean的过程中，可以调用BeanPostProcessor
        beanFactory.getBeansOfType(BeanPostProcessor.class).values().forEach(beanFactory::addBeanPostProcessor);
        Bean1 bean1 = beanFactory.getBean(Bean1.class);
        log.debug("bean1: {}", bean1);
        log.debug("bean1.getBean2(): {}", bean1.getBean2());

        // 正常来说，getBean时才会实例化Bean对象（懒汉式），但也可以提前创建（eager）
        beanFactory.preInstantiateSingletons();
    }

    @Configuration
    static class Config {
        @Bean
        public Bean1 bean1() {return new Bean1();}

        @Bean
        public Bean2 bean2() {return new Bean2();}
    }

    static class Bean1 {
        private static final Logger log = LoggerFactory.getLogger(Bean1.class);
        public Bean1() {
            log.warn("Bean1 created");
        }
        @Autowired
        private Bean2 bean2;

        public Bean2 getBean2() {
            return bean2;
        }

    }

    static class Bean2 {
        private static final Logger log = LoggerFactory.getLogger(Bean2.class);
        public Bean2() {
            log.warn("Bean2 created");
        }
    }

}
